﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Validation;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;

namespace CodeContrib.Web.Mvc
{
	public static class ModelStateExtensions
	{
		public static void AddModelError(this ModelStateDictionary modelState, string key, string errorMessage, params object[] args)
		{
			modelState.AddModelError(key, string.Format(errorMessage, args));
		}

		public static void AddModelError(this ModelStateDictionary modelState, DbEntityValidationResult validationResult)
		{
			AddModelError(modelState, validationResult, null);
		}

		public static void AddModelError(this ModelStateDictionary modelState, DbEntityValidationResult validationResult, string prefix)
		{
			if (validationResult == null)
			{
				return;
			}

			var orderedValidationErrors = new Dictionary<int, DbValidationError>();
			var unorderedValidationErrors = new List<DbValidationError>();

			foreach (var validationError in validationResult.ValidationErrors)
			{
				var displayAttribute = validationResult.Entry.Entity.GetType().GetProperty(validationError.PropertyName).GetCustomAttributes(typeof (DisplayAttribute), true).SingleOrDefault() as DisplayAttribute;

				if (displayAttribute == null)
				{
					unorderedValidationErrors.Add(validationError);
				}
				else
				{
					orderedValidationErrors.Add(displayAttribute.Order, validationError);
				}
			}

			var valiationErrors = orderedValidationErrors.OrderBy(e => new
				                                                           {
					                                                           e.Key,
					                                                           e.Value
				                                                           }).Select(x => x.Value).Concat(unorderedValidationErrors.OrderBy(e => e.PropertyName));

			foreach (var validationError in valiationErrors)
			{
				var propertyName = (prefix.AsNullIfEmpty() == null) ? validationError.PropertyName : string.Format("{0}.{1}", prefix, validationError.PropertyName);
				var errorMessage = validationError.ErrorMessage;

				if (modelState[propertyName] != null
				    && modelState[propertyName].Errors.Any(x => x.ErrorMessage == errorMessage))
				{
					continue;
				}

				modelState.AddModelError(propertyName, errorMessage);

				if (modelState[propertyName].Value == null)
				{
					var currentValue = validationResult.Entry.Member(propertyName).CurrentValue;

					modelState.SetModelValue(propertyName, new ValueProviderResult(currentValue, Convert.ToString(currentValue), CultureInfo.CurrentCulture));
				}
			}
		}

		public static void AddModelErrors(this ModelStateDictionary modelState, IEnumerable<DbEntityValidationResult> validationResults)
		{
			AddModelErrors(modelState, validationResults, null);
		}

		public static void AddModelErrors(this ModelStateDictionary modelState, IEnumerable<DbEntityValidationResult> validationResults, string prefix)
		{
			if (validationResults == null)
			{
				return;
			}

			foreach (var validationResult in validationResults)
			{
				AddModelError(modelState, validationResult, prefix);
			}
		}
	}
}